/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
  * Name: mp_audio_player.cpp
  *
  * Purpose: audio player implementation for micro player
  *
  * Developer:
  *   wen.gu , 2020-08-04
  *
  * TODO:
  *
  ***************************************************************************/


/******************************************************************************
**    INCLUDES
******************************************************************************/
#define LOG_TAG "MPAP"
#include "mp_audio_player.h"

#include "component/mp_decoder.h"
#include "component/mp_audio_sink.h"
#include "mp_plugin_manager.h"
#include "mp_worker.h"
#include "mp_buffer_queue.h"

#include "mp_log.h"
/******************************************************************************
**    MACROS
******************************************************************************/
#define INVALID_TIME_BASE (-1)
/******************************************************************************
**    VARIABLE DEFINITIONS
******************************************************************************/

class AudioPlayer::impl
{
public:
    MPDecoder* mAudDec = nullptr;
    MPAudioSink* mSink = nullptr;
    TrackPlayTimeSource mTimeSource = nullptr;
    MPWorker mPlayWorker;
    int64_t mTimeBase = -1;
    int64_t mPlaytimeMs = 0;
    int64_t mPlayLength = 0;
    MPTrackInfo mTrackInfo;
public:
    impl();
    ~impl();

public:
    void reset();
    eMPError initialize(MPTrackInfo& info);
    void write(MPBuffer* packet);
    eMPError start();
    eMPError pause();
    eMPError sync(); /** wait all cached data play to end */
    eMPError clear(); /** just clear(drop) all data cached in track player */
    int64_t playtimeMs(); /** get current playtime, with mill-second */
protected:
    void worker(); /** audio playing thread */

private:
    MPBufferQueue mBuffers;
};

/******************************************************************************
**    FUNCTION DEFINITIONS
******************************************************************************/
AudioPlayer::impl::impl()
    :mPlayWorker(std::bind(&impl::worker, this))
{
    //MPPluginManager& mp = MPPluginManager::getInstance();

    //mSink
}

AudioPlayer::impl::~impl()
{
    reset();
}

void AudioPlayer::impl::reset()
{
    mPlayWorker.pause();
    eMPError ret = MP_OK;
    if (mAudDec)
    {
        delete mAudDec;
        mAudDec = nullptr;
    }
    if (mSink)
    {
        delete mSink;
        mSink = nullptr;
    }

    mBuffers.clear();
    
}





void AudioPlayer::impl::worker()
{

    while (!mPlayWorker.isTerminate())
    {
        MPBuffer* esBuf = mBuffers.pop(10); /** if empty, then wait 10 ms */

        if (esBuf)
        {
            mAudDec->sendPacket(esBuf);
        }

        MPBuffer* rawBuf = nullptr; 
        eMPError ret = mAudDec->receiveFrame(&rawBuf);

        if (rawBuf)
        {
            if (mTimeBase == INVALID_TIME_BASE)
            {
                mTimeBase = rawBuf->pts();
            }
            MPAudioInfo& ainfo = mTrackInfo.audio;            

            mSink->write(rawBuf);
            mPlayLength += rawBuf->size(); 
            mPlaytimeMs = mTimeBase + mPlayLength / (ainfo.samplerate * ainfo.channelCount * (ainfo.bitDepth /8) );
        }
    }

}

eMPError AudioPlayer::impl::initialize(MPTrackInfo& info)
{
    MPPluginManager& amp = MPPluginManager::getInstance();
    MPDecoderCreateParam decCp;
    decCp.info = info;

    eMPError ret = amp.find(&decCp, (MPComponent**)&mAudDec);

    if (ret == MP_OK)
    {
        MPTrackInfo* outInfo = mAudDec->getFormat();
        if (outInfo)
        {
            MPAudioSinkCreateParam mpai;

            //MPTrackInfo& ti = mpai.info;
            mpai.info = *outInfo;

            ret = amp.find(&mpai, (MPComponent**)&mSink);

            if (ret == MP_OK)
            {
                mTrackInfo = *outInfo;
            }
            else
            {
                delete mAudDec;
                mAudDec = nullptr;
                LOGE("create audio sink component failed(%s)\n", MPError2Str(ret));
            }
        }
        else
        {
            delete mAudDec;
            mAudDec = nullptr;
            LOGE("get audio output info for audio sink failed\n");
        }

    }
    else
    {
        LOGE("create audio decodere component failed(%s)\n", MPError2Str(ret));
    }

    return ret;

}

void AudioPlayer::impl::write(MPBuffer* packet)
{
    mBuffers.push(packet);
}

eMPError AudioPlayer::impl::start()
{
    eMPError ret = MP_OK;
    if (mSink)
    {
       ret = mSink->start();
    }

    mPlayWorker.resume();
    return ret;
}

eMPError AudioPlayer::impl::pause()
{  
    eMPError ret = MP_OK;
    if (mSink)
    {
        ret = mSink->pause();
    }
    mPlayWorker.pause();
    return ret;
}

eMPError AudioPlayer::impl::sync() /** wait all cached data play to end */
{
    mPlayWorker.pause();
    eMPError ret = MP_OK;
    if (mSink)
    {
        ret = mSink->flush();
    }

    return ret;
}

eMPError AudioPlayer::impl::clear() /** just clear(drop) all data cached in track player */
{
    mPlayWorker.pause();
    eMPError ret = MP_OK;
    if (mAudDec)
    {
        //mAudDec->
    }
    if (mSink)
    {
        ret = mSink->flush();
    }

    mBuffers.clear();
    return ret;
}

int64_t AudioPlayer::impl::playtimeMs() /** get current playtime, with mill-second */
{
    return mPlaytimeMs;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

AudioPlayer::AudioPlayer()
    :mImpl(new impl)
{
    /** todo, something */
}

AudioPlayer::~AudioPlayer()
{
    /** todo, something */
}


void AudioPlayer::reset()  /** */
{
    mImpl->reset();
}

void AudioPlayer::setPlayTimeSource(TrackPlayTimeSource timeSource)
{
    mImpl->mTimeSource = timeSource;
}

eMPError AudioPlayer::initialize(MPTrackInfo& info)
{
    return mImpl->initialize(info);
}

void AudioPlayer::write(MPBuffer* packet)
{
    mImpl->write(packet);
}

eMPError AudioPlayer::start()
{
    return mImpl->start();
}

eMPError AudioPlayer::pause()
{
    return mImpl->pause();
}

eMPError AudioPlayer::sync() /** wait all cached data play to end */
{
    return mImpl->sync();
}

eMPError AudioPlayer::clear() /** just clear(drop) all data cached in track player */
{
    return mImpl->clear();
}

int64_t AudioPlayer::playtimeMs()
{
    return mImpl->playtimeMs();
}

